{
  "cells": [
    {
      "attachments": {},
      "cell_type": "markdown",
      "metadata": {
        "nteract": {
          "transient": {
            "deleting": false
          }
        }
      },
      "source": [
        "# Simulate the ground state of a Hydrogen molecule using Variational Quantum Eigensolver (VQE)\n",
        "\n",
        "In this notebook, you'll learn how to run VQE for a $H_{2}$ molecule using Qiskit on an Azure Quantum backend.\n",
        "\n",
        "VQE is a variational algorithm for quantum chemistry that uses an optimization loop to minimize a cost function. The cost function is an energy evaluation $E = \\left\\langle\\psi|H|\\psi\\right\\rangle$ where $|\\psi (\\theta)\\rangle$ is a parametric trial state that estimates the ground state of the molecule. For each evaluation, we modify the trial state until the energy reaches a minimum.\n",
        "\n",
        "![VQE diagram](https://user-images.githubusercontent.com/4041805/166981008-023aba4c-26f8-498e-93ee-a1d9a39ddbcd.png)\n",
        "\n",
        "For more information about running VQE using Qiskit, see: [Qiskit Textbook - VQE Molecules](https://qiskit.org/textbook/ch-applications/vqe-molecules.html#implementationnoisy).\n",
        "\n",
        "To read more about the optimization method used in this example, see [Wikipedia - SPSA](https://en.wikipedia.org/wiki/Simultaneous_perturbation_stochastic_approximation)."
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "metadata": {
        "nteract": {
          "transient": {
            "deleting": false
          }
        }
      },
      "source": [
        "## Define  problem\n",
        "\n",
        "You will use the `PySCFDriver` to define the geometry of the $H_2$ molecule and generate the input for the VQE run. Alternatively, you could also use an `FCIDUMP` file as input. More information about the FCIDUMP file format is [here](https://www.sciencedirect.com/science/article/abs/pii/0010465589900337).\n",
        "\n",
        "The [Jordan-Wigner transformation](https://en.wikipedia.org/wiki/Jordan%E2%80%93Wigner_transformation) is specified as the `QubitConverter`to use."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "jupyter": {
          "outputs_hidden": false,
          "source_hidden": false
        },
        "nteract": {
          "transient": {
            "deleting": false
          }
        }
      },
      "outputs": [],
      "source": [
        "from qiskit_nature.second_q.drivers import PySCFDriver\n",
        "from qiskit_nature.second_q.formats import  fcidump_to_problem\n",
        "from qiskit_nature.second_q.formats.fcidump import FCIDump\n",
        "from qiskit_nature.second_q.mappers import JordanWignerMapper, QubitConverter\n",
        "from qiskit_nature.units import DistanceUnit\n",
        "\n",
        "def get_problem():\n",
        "    # Replace with get_problem_fcidump() to use the FCIDUMP file as input.\n",
        "    return get_problem_pyscf();\n",
        "\n",
        "def get_problem_pyscf():\n",
        "    driver = PySCFDriver(\n",
        "        atom=\"H 0 0 0; H 0 0 0.735\",\n",
        "        basis=\"sto3g\",\n",
        "        charge=0,\n",
        "        spin=0,\n",
        "        unit=DistanceUnit.ANGSTROM,\n",
        "    )\n",
        "    \n",
        "    return driver.run()\n",
        "\n",
        "def get_problem_fcidump():\n",
        "    import requests\n",
        "    url = 'https://aka.ms/fcidump/vqe/h2-2e-2o'\n",
        "    r = requests.get(url, allow_redirects=True)\n",
        "    open('vqe_h2.fcidump', 'wb').write(r.content)\n",
        "    return fcidump_to_problem(FCIDump.from_file(\"vqe_h2.fcidump\"))\n",
        "\n",
        "def get_qubit_converter():\n",
        "    return QubitConverter(JordanWignerMapper(), two_qubit_reduction=True, z2symmetry_reduction='auto')\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "nteract": {
          "transient": {
            "deleting": false
          }
        }
      },
      "source": [
        "## Exact result\n",
        "First, let's compute the exact result that could be used to compare with the results of VQE later on. "
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "jupyter": {
          "outputs_hidden": false,
          "source_hidden": false
        },
        "nteract": {
          "transient": {
            "deleting": false
          }
        }
      },
      "outputs": [],
      "source": [
        "from qiskit_nature.second_q.algorithms import NumPyMinimumEigensolverFactory\n",
        "from qiskit_nature.second_q.algorithms import GroundStateEigensolver\n",
        "\n",
        "problem = get_problem()\n",
        "qubit_converter = get_qubit_converter()\n",
        "exact_solver = NumPyMinimumEigensolverFactory()\n",
        "calc = GroundStateEigensolver(qubit_converter, exact_solver)\n",
        "result = calc.solve(problem)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "jupyter": {
          "outputs_hidden": false,
          "source_hidden": false
        },
        "nteract": {
          "transient": {
            "deleting": false
          }
        }
      },
      "outputs": [],
      "source": [
        "print(\"Exact result:\\n\")\n",
        "print(result.groundenergy)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "nteract": {
          "transient": {
            "deleting": false
          }
        }
      },
      "source": [
        "## Run on Qiskit's Local Aer Simulator without Noise\n",
        "\n",
        "Here, you will simulate the VQE run locally using the noiseless Aer simulator. Here the maximum number of iterations of the optimizer is set to 100."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "jupyter": {
          "outputs_hidden": false,
          "source_hidden": false
        },
        "nteract": {
          "transient": {
            "deleting": false
          }
        }
      },
      "outputs": [],
      "source": [
        "from qiskit.algorithms.optimizers import SPSA\n",
        "from qiskit_aer.primitives import Estimator as AerEstimator\n",
        "from qiskit_nature.second_q.algorithms import GroundStateEigensolver, VQEUCCFactory\n",
        "from qiskit_nature.second_q.circuit.library import UCCSD\n",
        "\n",
        "problem = get_problem()\n",
        "qubit_converter = get_qubit_converter()\n",
        "\n",
        "spsa_optimizer=SPSA(maxiter=100)\n",
        "\n",
        "noiseless_estimator = AerEstimator(\n",
        "    run_options={ \"shots\": 1000 }\n",
        ")\n",
        "\n",
        "vqe_solver = VQEUCCFactory(estimator=noiseless_estimator, ansatz=UCCSD(), optimizer=spsa_optimizer)\n",
        "calc = GroundStateEigensolver(qubit_converter, vqe_solver)\n",
        "result = calc.solve(problem)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "jupyter": {
          "outputs_hidden": false,
          "source_hidden": false
        },
        "nteract": {
          "transient": {
            "deleting": false
          }
        }
      },
      "outputs": [],
      "source": [
        "print(\"Local AER simulator result:\\n\")\n",
        "print(result.groundenergy)"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "metadata": {
        "editable": true,
        "nteract": {
          "transient": {
            "deleting": false
          }
        },
        "run_control": {
          "frozen": false
        }
      },
      "source": [
        "## Run on an Azure Quantum backend\n",
        "Now, you will run the same problem on a backend of your choice through Azure Quantum. Here the maximum number of iterations of the optimizer is set to 50.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "jupyter": {
          "outputs_hidden": false,
          "source_hidden": false
        },
        "nteract": {
          "transient": {
            "deleting": false
          }
        }
      },
      "outputs": [],
      "source": [
        "from azure.quantum.qiskit import AzureQuantumProvider\n",
        "\n",
        "# Connect to the Azure Quantum workspace via a Qiskit provider\n",
        "provider = AzureQuantumProvider(\n",
        "            resource_id = \"\",\n",
        "            location = \"\")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "jupyter": {
          "outputs_hidden": false,
          "source_hidden": false
        },
        "nteract": {
          "transient": {
            "deleting": false
          }
        }
      },
      "outputs": [],
      "source": [
        "ionq_sim = provider.get_backend('ionq.simulator')\n",
        "quantinuum_sim = provider.get_backend('quantinuum.sim.h1-1e')\n",
        "rigetti_sim = provider.get_backend('rigetti.sim.qvm')\n",
        "\n",
        "# Set the backend you want to use here.\n",
        "# WARNING: Quantinuum simulator usage is not unlimited. Running this sample against it could consume a significant amount of your eHQC quota.\n",
        "backend_to_use = ionq_sim"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "metadata": {
        "nteract": {
          "transient": {
            "deleting": false
          }
        }
      },
      "source": [
        "You will now kickoff the VQE run on the selected backend through Azure Quantum. Since this will trigger several jobs, a `session` is used to group them together into a single logical entity.\n",
        "\n",
        "#### IMPORTANT NOTES !!\n",
        "\n",
        "1. This cell will take about 20 minutes to run on the IonQ simulator for max_iter=50. It generates around 300 jobs. The time can vary depending on the backend queue times. Running with max_iter=50 may not be sufficient for the results to converge. You could consider increasing the number of iterations to 100 to give a more accurate result, but please be aware of the increased running times for the cell.\n",
        "1. If you are an Azure Quantum credits user, you may not have enough credits to run this sample on certain backends.\n",
        "1. **If you run this against a QPU hardware backend instead of a simulator, you will likely incur a large cost or consume a large number of your alloted credits.**\n",
        "1. You may lose results if you lose network connectivity while the cell is running. It may be better to download the notebook and run it locally for better reliability.\n",
        "1. If the `calc.solve` fails, one or more jobs may have failed. If so, you can find the session under `Job management` in your workspace, click on it to open the Session view and then click on a Job that failed to find the reason for failure.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "jupyter": {
          "outputs_hidden": false,
          "source_hidden": false
        },
        "nteract": {
          "transient": {
            "deleting": false
          }
        }
      },
      "outputs": [],
      "source": [
        "\n",
        "from qiskit.algorithms.optimizers import SPSA\n",
        "from qiskit_nature.second_q.algorithms import GroundStateEigensolver, VQEUCCFactory\n",
        "from qiskit_nature.second_q.circuit.library import UCCSD\n",
        "from qiskit.primitives.backend_estimator import BackendEstimator\n",
        "\n",
        "problem = get_problem()\n",
        "qubit_converter = get_qubit_converter()\n",
        "\n",
        "spsa_optimizer=SPSA(maxiter=50)\n",
        "\n",
        "backend_estimator = BackendEstimator(backend=backend_to_use, options={ \"shots\": 1000 })\n",
        "vqe_solver = VQEUCCFactory(estimator=backend_estimator, ansatz=UCCSD(), optimizer=spsa_optimizer)\n",
        "calc = GroundStateEigensolver(qubit_converter, vqe_solver)\n",
        "\n",
        "# Wrap the call to solve in a session \"with\" block so that all jobs submitted by it are grouped together.\n",
        "# Learn more about Interactive Hybrid and the Session API at https://aka.ms/AQ/Hybrid/Sessions/Docs\n",
        "with backend_to_use.open_session(name=\"VQE H2\") as session:\n",
        "    result = calc.solve(problem)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "jupyter": {
          "outputs_hidden": false,
          "source_hidden": false
        },
        "nteract": {
          "transient": {
            "deleting": false
          }
        }
      },
      "outputs": [],
      "source": [
        "print(\"AzureQuantum \" + backend_to_use.name() + \" result:\\n\")\n",
        "print(result.groundenergy)"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "metadata": {
        "nteract": {
          "transient": {
            "deleting": false
          }
        }
      },
      "source": [
        "In this notebook, you've run VQE on an Azure Quantum backend to calculate the ground state of a $H_2$ molecule. Nice job! 👏🏽\n",
        "\n",
        "As a next step, you can modify the sample to run your own molecule, or run it on QPU hardware.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## ℹ️ Relationship with resource estimation sample\n",
        "\n",
        "Variational Quantum Eigensolvers (VQEs) are useful for building an understanding of the current limits of quantum hardware for the purposes of education and research. In the longer term, we will simulate quantum systems using more efficient algorithms tailored for large-scale fault-tolerant quantum hardware.\n",
        "\n",
        "Notable simulation algorithms, such as qubitization, quantum signal processing, or trotterization, can be used as subroutines in the quantum phase estimation (QPE) algorithm to obtain high-accuracy energy estimates of a targeted eigenstate. The Azure Quantum Resource Estimator is designed with these long-term algorithms (particularly QPE + qubitization) in mind (rather than VQE).\n",
        "\n",
        "Please be aware that while you may use the FCIDUMP files included in this sample to generate resource estimates in the [chemistry resource estimation sample](https://github.com/microsoft/Quantum/blob/main/samples/azure-quantum/resource-estimation/estimation-chemistry.ipynb), the samples provided here are for very small systems that we can simulate using VQE optimized for quantum hardware today. As the current QPE + qubitization implementation is optimized for large-scale fault-tolerant quantum hardware, the quantum resource estimates generated by the Resource Estimator will be much larger than what is required to run VQE for these systems.\n",
        "\n",
        "The FCIDUMP sample files provided in the chemistry end-to-end sample are too large to run using VQE on today's quantum systems, so they will not work in this VQE notebook."
      ]
    }
  ],
  "metadata": {
    "kernel_info": {
      "name": "python3"
    },
    "kernelspec": {
      "display_name": "Python 3 (ipykernel)",
      "language": "python",
      "name": "python3"
    },
    "language_info": {
      "codemirror_mode": {
        "name": "ipython",
        "version": 3
      },
      "file_extension": ".py",
      "mimetype": "text/x-python",
      "name": "python",
      "nbconvert_exporter": "python",
      "pygments_lexer": "ipython3",
      "version": "3.9.13"
    },
    "nteract": {
      "version": "nteract-front-end@1.0.0"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}
